import api from './api'
import basedata from './basedata/'
import fixcolumns from './fixcolumns'
import {
	handleConfig,
	handleFail,
	handleSuccess,
	loadingView
} from './handle/'
import asyncDb from './async/'
import {getError, getResult} from './async/handle'
import {getCloud, getSpaceBase} from './sync/'
import {showToast, showLoading, hideLoading, deepClone} from '@/core/tool/base'
import router from '@/core/router'
import user from '@/core/user'

// 缓存处理
import {cache, RequestActions, requestMd5s, deleteCache} from "./cache"

/**
 * 记录需要等待的请求
 * 有新的需要等待的请求，就往后添加，但是执行时，每次执行第一个，执行完就移除
 */
const waitList = []
// 记录正在执行请求的文字。如果存在，需保留最新的请求文字
var TextForLoading = ''

// 是否输出错误定位。一般仅正式环境中开启
const ConsoleError = (text) => {
	if (process.env.NODE_ENV === 'development') {
		// 直接断点定位即可
	} else {
		console.log(`错误定位：${text}`)
	}
}

/**
 * 获取数据库信息
 * 实际上就是查询对应云服务空间中a-a-table的对应的数据库名称的信息
 */
const dbGet = (name, space = basedata.current, cache = -1, loading) => {
	return new Promise((resolve, reject) => {
		if (name) {
			if (name.substr(-5) === '-test') {
				name = name.slice(0, -5)
			}
			console.log('开始查询数据库信息：' + name) // type: 1：约定是本地直接请求
			fetchData('fetch', 'table', {name, space}, loading, {cache, showConsole: false, type: 1}).then(res => {
				if (res.data) {
					console.log(`数据库“${name}”${res.timestamp ? '缓存' : '查询'}结果：`)
					console.log(res.data)
					resolve(getResult(res))
				} else {
					reject(getError(`数据库“${name}”没有备案`))
				}
			}).catch(reject)
		} else {
			console.warn('请传入数据库名称')
			reject()
		}
	})
}

/**
 * 实际请求
 * group：请求的组名。比如登录相关，一般是login。如果是直接url请求，请传http
 * fname：实际请求的接口名。比如登录初始化：initial。也可以是完整的url
 * where：请求参数。可以是对象或者非对象。云对象中如果除了necessary之外还有多个参数，该where就一定是数组
 * loading：是否显示loading。如果该值不为布尔值，则取api中默认值
 * configInfo：请求的专属配置信息。比如请求头、请求方式等。优先级最高，其次是api中配置，最后是默认配置
 * 
 * 本地请求的情况：有且仅有指定type='get'(并且isLocal!=false)或者isLocal=true时
 */
const fetchData = (group, fname, where, loading, configInfo = {}) => {
	const again = () => {
		/**
		 * 重试的方法。要求不能改变传入的任何参数
		 * 请求失败时需要重试
		 * 需要登录的请求，如果未登录，需要先登录，登录完成后需要重试
		 */
		return fetchData(group, fname, where, loading, configInfo)
	}
	return new Promise((resolve, reject) => {
		if (group && fname) {
			const logArr = [] // 请求参数校验
			var cloneObj // api对象
			if (fname.substr(0, 4) === 'http') {
				cloneObj = {url: fname}
			} else {
				if (api[group] && api[group][fname]) {
					cloneObj = deepClone(api[group][fname])
				} else {
					return handleFail(`不存在${group}或者${fname}接口`, {...configInfo, name: fname}, reject)
				}
			}
			var { // 云端被固定的信息
				space, name, detail,
				url, urlTest, method, pin,
				config = {}, text, params,
			} = cloneObj
			const {state, dispatch} = getApp().$store
			const {Authorization, NoLocal} = state
			// 只有是fetch组别下本地有相关的函数才可以本地直接数据库操作，其它都是云函数云对象或者url化操作
			const type = !url && group === 'fetch' && asyncDb[fname] ? fname : 'function'
			var isLocal // 是否本地请求
			if (NoLocal) { // 没有本地请求，全部执行云端请求。有一点例外，如果指定了其它云服务空间，则必须是本地请求！
				isLocal = false
				configInfo.type = 2
			} else {
				if (configInfo.type === 1 && type !== 'function') { // 指定是本地请求。但是仅fetch中的几个本地有的函数才生效
					isLocal = true
				}
			}
			const configNew =  Object.assign(configInfo, config)
			// 参数的必要性判断
			var errorString = asyncDb.calibrate(group, fname, where)
			if (errorString) {
				return handleFail(errorString, {...configNew, name: text}, reject, again)
			} else {
				if (where) {
					logArr.push('请求参数：')
					logArr.push(where)
				} else {
					logArr.push('请求参数：无')
				}
			}
			if (url) {
				if (url.substr(0, 4) === 'http') {
					// 已是全路径，不需要处理
				} else {
					url = api.host + url
				}
				logArr.push(`${text || ''}开始请求：${url}`)
				if (!text) {
					text = url
				}
				if (pin) {
					var whereKeys
					if (where || where === 0) {
						if (typeof(where) === 'object') {
							whereKeys = Object.keys(where)
						}
					}
					/**
					 *  pin=1或者true：只有一个参数，直接放在后面。肯定会删除原有的参数；
					 *	pin=2：可能有多个参数，以`?{key}={value}`的形式放在后面，肯定会删除原有的参数；
					 *	pin=字符串(可以带逗号)，从参数对象中取出key，以2的形式拼接，并删除当前参数，保留未用到的参数继续当做body发送
					 */
					switch (pin){
						case true:
						case 1:
							if (whereKeys) {
								url += where[whereKeys[0]]
							} else {
								url += `${where}`
							}
							where = undefined
							params = undefined
							break;
						case 2:
							if (whereKeys) {
								const urlArr = []
								whereKeys.forEach(key => {
									urlArr.push(`${key}=${where[key]}`)
								})
								url += `?${urlArr.join('&')}`
							} else {
								url += `${where}`
							}
							where = undefined
							params = undefined
						default:
							if (typeof(pin) === 'string') {
								const urlArr = []
								pin.split(',').forEach(key => {
									if (whereKeys.includes(key)) {
										urlArr.push(`${key}=${where[key]}`)
										delete where[key]
										if (params && typeof(params) === 'object') {
											delete params[key]
										}
									}
								})
								url += `?${urlArr.join('&')}`
							}
							break;
					}
				}
			} else {
				if (space) {
					if (space !== basedata.current) {
						// 当不是当前绑定的云服务空间时，数据库操作只能本地请求
						isLocal = true
					}
				} else { // 默认是当前项目所在的云服务空间名称
					space = basedata.current
				}
				if (basedata[space] && name) {
					if (type === 'function') { // 云函数云对象的模式
						configInfo.type = 0 // 一定是云端请求
						if (detail) { // 云对象模式
							logArr.push(`${text || name}开始请求：云对象` + (text ? ` - ${name} - ${detail}` : `- ${detail}`))
						} else { // 云函数模式
							logArr.push(`${text || name}开始请求：云函数` + (text ? ` - ${name}` : ''))
						}
					} else { // 云数据库模式。默认get才直接在本地访问，其它都是通过云对象实现
						if (type === 'get' && isLocal !== false) {
							isLocal = true // 默认是本地请求
						}
						if (isLocal) { // 在本地实现
							configInfo.type = 1
						} else { // 其它默认在云端实现
							configInfo.type = 2
							if (!where || Object.prototype.toString.call(where) !== '[object Object]') {
								where = {}
							}
							// 将数据库操作的方式，作为云对象的方法名
							detail = type
						}
						logArr.push(`${text || name}开始请求：云数据库` + (text ? ` - ${name}` : ''))
					}
					if (!text) {
						text = name
					}
				} else {
					if (name) {
						return handleFail(`不存在云服务空间：${space}`, {...configNew, name: text || name}, reject)
					}
					return handleFail(`云服务空间参数不完整`, {...configNew, name: text}, reject)
				}
			}
			/**
			 * 对config进行初始化
			 * 优先读取传入的configInfo，然后再给系统性处理一遍
			 */
			const oldConfig = deepClone(config)
			if (loading === true || loading === false) {
				// 以传入的值为准
			} else {
				loading = cloneObj.loading
			}
			if (configInfo.cache === undefined) {
				// 传入值不存在时以接口配置的为准
				configInfo.cache = cloneObj.cache
			}
			if (configInfo.refresh === true || configInfo.refresh === false) {
				// 以传入的值为准
			} else {
				configInfo.refresh = cloneObj.refresh
			}
			if (configInfo.isTest === true || configInfo.isTest === false) {
				// 以传入的值为准
			} else {
				configInfo.isTest = cloneObj.isTest
			}
			if (!configInfo.header) {
				configInfo.header = cloneObj.header
			}
			Object.keys(configInfo).forEach(k => {
				oldConfig[k] = configInfo[k]
			})
			const newConfig = handleConfig(oldConfig, url ? 'url' : type)
			// 用于知晓本次请求的名称
			newConfig.name = text
			/**
			 * loadingText: 加载中的文字
			 * loadingOnly: 是否只允许单请求操作。如果是则需要等之前的请求完毕后才能继续请求
			 * errType: 错误信息类型。开发状态下默认是toast，生产状态下默认是modal
			 */
			var {loadingText, loadingOnly, showConsole, errType, header, timeout, isTest} = newConfig
			// 另外需增加是否测试环境的判断
			if (isTest === true || isTest === false) {
				// 以临时决定的值为准
			} else {
				// 以项目配置处理后的值为准
				isTest = state.isTest
				newConfig.isTest = isTest
			}
			var cloud = uniCloud.init(getSpaceBase(basedata[space]))
			if (isTest) { // 测试环境的处理
				if (url && urlTest) { // 优先使用urlTest
					if (urlTest.substr(0, 4) === 'http') {
						url = urlTest
					} else {
						url = api.host + urlTest
					}
				} else {
					if (cloud) {
						const {testDb = [], testFc = []} = basedata[space]
						if (detail) { // 云对象模式
							if (testFc.includes(name)) {
								name += '-test'
							} else {
								testFc.forEach(str => {
									if (str.includes(`${name}:`)) {
										var art = str.split(':')
										if (art.length > 1 && art[1].split(',').includes(detail)) {
											detail += '_test' // 与云函数云对象云数据库略有不同
										}
									}
								})
							}
						} else if (type === 'function') { // 云函数模式
							if (testFc.includes(name)) {
								name += '-test'
							}
						} else { // 云数据库模式
							if (testDb.includes(name)) {
								/**
								 * 内部也会处理，这里目前也可以不添加
								 * 哪天要对内部做大变动时可以考虑这一点，只允许在这里确定，不用再内部处理
								 * 搜索getDbName即可处理，但是还要考虑后端公共模块要同步更新
								 */
								name += '-test'
							}
						}
					}
				}
			}
			logArr.unshift(`${isTest ? '测试环境' : '正式环境'}`)
			if (showConsole) {
				logArr.forEach(str => {
					console.log(str)
				})
			}
			ConsoleError('1')
			/**
			 * 执行loading
			 * action：需要执行的请求，表示是开始执行
			 * 关于loadingOnly，是否只允许单请求操作
			 * 如果同时有多个请求传入，会按顺序被加入到等待队列中，并依次执行。执行结束后，会删除等待队列中的第一个请求
			 */
			const handleLoading = (action) => {
				ConsoleError('2')
				if (loading) {
					if (action) {
						if (TextForLoading === loadingText) {
							// 不用处理，因为已经显示了
						} else {
							hideLoading()
							showLoading(loadingText)
							TextForLoading = loadingText
						}
					} else {
						hideLoading()
						TextForLoading = ''
					}
				}
				if (typeof(action) === 'function') {
					// 当前请求顺序
					if (loadingOnly) {
						if (waitList.length > 0) {
							// 加入等待队列。请求结束后会自动执行该队列的第一个请求
							waitList.push(action)
							// 不能返回，就是等待中，等待当前正在执行的请求结束后，如果是loadingOnly时自动执行该队列的第一个请求
						} else {
							// 没有等待队列，直接执行
							action()
						}
					} else {
						// 不用等待队列，直接执行
						action()
					}
				} else {
					if (action) {
						// 明确是执行了等待队列中的请求，则要移除等待队列中的第一个请求
						if (waitList.length > 0) {
							waitList.shift()
						}
					}
					// 自动执行第一个等待队列
					if (waitList.length > 0) {
						// 明确执行的是等待队列中的请求
						waitList[0](true)
					}
				}
			}
			handleLoading((forWait) => { // 实际执行的动作。forWait：是否是等待队列中的请求，是的话，执行完毕则要删除等待队列的第一项
				ConsoleError('3')
				const necessary = asyncDb.getNecessary(newConfig)
				if (type === 'get' || (url && (!method || method === 'GET' || method === 'get'))) {
					if (!newConfig.cache) {
						// 默认做100秒的缓存处理
						newConfig.cache = 100
					}
				}
				/**
				 * 实际执行的请求结果
				 */
				const complement = ({res, error, callback}) => {
					ConsoleError('4')
					if (res) {
						handleSuccess(res, newConfig, resolve, reject, again, (res) => {
							ConsoleError('5')
							/**
							 * 根据设定登录接口，拿到Authorization后要设置到请求头
							 * 并且获取会员信息，写入$store
							 */
							if (cloneObj.nologin === 1) { // 正在登录的接口，一般仅限于登录注册，这种接口必须指定nologin = 1
								// 登录成功后，一般要获取用户信息，并写入$store
								dispatch('handleLoginSuccess', res)
							}
							// 写入缓存的回调
							callback && callback({res})
							// 接口配置的回调。一般仅自定义的接口才会用到
							cloneObj.callback && cloneObj.callback(res)
							// 参数设置的回调
							newConfig.complete && newConfig.complete(res)
						})
					} else {
						handleFail(error, newConfig, reject, again)
						// 写入缓存的回调
						callback && callback({error})
						// 参数设置的回调
						newConfig.complete && newConfig.complete(error)
					}
					handleLoading(forWait)
				}
				// 优先读取缓存信息
				cache(group + fname, where, necessary, newConfig).then(res => {
					ConsoleError('6')
					handleLoading(forWait)
					complement({res})
				}).catch(res => {
					ConsoleError('7')
					const {callback, CacheMd5} = res
					if (!header) {
						header = {}
						// header = {'Content-Type': 'application/json'}
					}
					if (Authorization) {
						if (url) { // 设置请求头。在不同的项目须设置的字段名不一样
							// if (Authorization.includes('Bearer')) {
							// 	header['Authorization'] = Authorization
							// } else {
							// 	header['Authorization'] = 'Bearer ' + Authorization
							// }
						}
					} else {
						const self = router.getCurrentPageObj()
						if (cloneObj.nologin || (self && self.nologin) || (url && url.includes('/api/no-auth'))) { // 判定默认不需要登录的api再不同单位项目中不一样
							// 不需要登录或者正在登录中
							ConsoleError('21')
						} else { // 需要登录，但是没有登录
							if (1) {
								ConsoleError('22')
								dispatch('handleLoginExpired', () => {
									ConsoleError('23')
									again().then(resolve).catch(reject)
								})
							} else { // 要求在微信页面的处理。要求一定要在微信打开的话，需要先授权再登录
								ConsoleError('24')
								core.wechat.initial().then(res => {
									ConsoleError('25')
									dispatch('handleLoginExpired', () => {
										ConsoleError('26')
										again().then(resolve).catch(reject)
									})
								}).catch(reject)
							}
							return delete RequestActions[CacheMd5]
						}
					}
					if (url) { // url请求
						ConsoleError('27')
						uni.request({
							url, timeout, header, 
							method, data: where,
							success: res => {
								ConsoleError('8')
								if (res.data.code === 200) {
									complement({res, callback})
								} else {
									errorString = res.data.msg || res.errMsg
									const error = new Error(errorString)
									error.code = res.data.code
									complement({error, callback})
								}
							},
							fail: error => {
								ConsoleError('9')
								complement({error, callback})
							}
						})
					} else {
						ConsoleError('10')
						if (cloud) {
							if (configInfo.type === 1 && asyncDb[type]) { // 本地直接请求
								ConsoleError('11')
								if (where.space && where.space !== space) {
									space = where.space
									cloud = uniCloud.init(getSpaceBase(basedata[space]))
								}
								if (where.name) {
									name = where.name
								}
								const cloudObj = {cloud, space, name, dbGet}
								if (type === 'remove' && where && where.ChildKey) {
									cloudObj.get = asyncDb.get
								}
								asyncDb[type](cloudObj, where, newConfig, true).then(res => {
									complement({res, callback})
								}).catch(error => {
									complement({error, callback})
								})
							} else if (detail) { // 请求云对象
								ConsoleError('12')
								if (Array.isArray(where)) {
									where.push(necessary)
								} else {
									where = [where, necessary]
								}
								ConsoleError('13')
								cloud.importObject(name, loadingView(loading, config))[detail](...where).then(res => {
									ConsoleError('14')
									complement({res, callback})
								}).catch(error => {
									ConsoleError('15')
									complement({error, callback})
								})
							} else if (type === 'function') { // 请求云函数
								ConsoleError('16')
								where.necessary = necessary
								cloud.callFunction({
									name, data: where, timeout, success: res => { // 云函数返回值中会包含一层result
										ConsoleError('17')
										if (res.result) {
											res = res.result
										}
										complement({res, callback})
									}, fail: error => {
										ConsoleError('18')
										complement({error, callback})
									}
								})
							} else {
								ConsoleError('19')
								handleFail('错误的请求类型：' + type, newConfig, reject, again)
							}
						} else {
							ConsoleError('20')
							handleFail('云服务空间不存在：' + space, newConfig, reject, again)
						}
					}
				})
			})
		} else {
			console.error(`group：${group || '没有请求组名'}；fname：${fname || '没有请求方法'}`)
			console.error(where)
		}
	})
}

// 统一处理table以及configd等再请求
const handleWithTable = ({type, table, where, pagination, data, addType, isReturn, isDelete, MainKey, loading, config}) => {
	if (config) {
		const arr = Object.keys(config)
		// 以配置信息为准
		if (arr.includes('isReturn')) {
			isReturn = config.isReturn
			delete config.isReturn
		}
		if (arr.includes('isDelete')) {
			isDelete = config.isDelete
			delete config.isDelete
		}
		if (arr.includes('MainKey')) {
			MainKey = config.MainKey
			delete config.MainKey
		}
	} else {
		config = {}
	}
	var space, name
	if (typeof table === 'string') {
		name = table
		table = {name}
	} else {
		if (typeof(table.isTest) === 'boolean') {
			config.isTest = isTest
		}
		space = table.space
		name = table.name
	}
	if (where && typeof(where) === 'string') {
		if (where === 'all') {
			// 原样返回
		} else if (where.startsWith('jql:')) {
			where.substring(4)
		} else {
			where = {_id: {filterType: 'select', filter: where.split(',')}}
		}
	}
	/**
	 * where参数说明
	 * 一般要求where是对象
	 * 字符串的情况仅限于删除情况下的'all'，以及_id和jql语法
	 * jql语法有前缀：`jql:`
	 * 其他都表示_id，可以用逗号隔开
	 */
	return fetchData('fetch', type, {...table, where, pagination, data, addType, isReturn, isDelete, MainKey}, loading, config)
}

module.exports = {
	RequestActions, requestMd5s, deleteCache,
	api, basedata, fixcolumns, dbGet, getCloud,
	fetchData, // 所有请求。不支持任意添加，需在公共模块中注册！如需模拟，请在ApiMoni文件中写测试代码
	token: '', // 记录每一次请求的token
	
	/****以下方法仅是投巧的写法，没法有效管控。以及使用云对象fetch+type同样如此***/
	
	/**
	 * 增加一条或多条数据
	 * @param {Object || String} table：数据库信息。对象表示写成了space和name形式，字符串表示name
	 * @param {Object || Array} data：需要新增的信息
	 * @param {Boolean} isReturn：是否返回新增的完整数据
	 * @param {Boolean} loading：是否显示loading框
	 * @param {Object} config：其它配置信息
	 */
	add(table, data, isReturn, loading, config) {
		var addType
		if (data && data.addType) {
			addType = data.addType
			delete data.addType
		}
		return handleWithTable({type: 'add', table, data, addType, isReturn, loading, config})
	},
	/**
	 * 删除一个或多个数据
	 * @param {Object || String} table：数据库信息。对象表示写成了space和name形式，字符串表示name
	 * @param {Object || Array || String} where：删除信息的条件，同查询，增加一种为“all”的情况。如果是jql语法，需前缀’jql:‘标识
	 * @param {Boolean} isDelete：是否真的删除数据
	 * @param {Boolean} loading：是否显示loading框
	 * @param {Object} config：其它配置信息
	 * 
	 * 删除时务必注意select类型查询时，必须保证每一个都有值，否则会删除所有数据！
	 */
	remove(table, where, isDelete, loading, config) {
		return handleWithTable({type: 'remove', table, where, isDelete, loading, config})
	}, 
	/**
	 * 修改一个或多个数据
	 * @param {Object || String} table：数据库信息。对象表示写成了space和name形式，字符串表示name
	 * @param {Object || Array || String} where：需修改的信息的条件，同查询。同时默认不返回修改后的数据。如果是jql语法，需前缀’jql:‘标识
	 * @param {Object} data：需修改的信息。有_id的话，会被自动去除
	 * @param {Boolean} loading：是否显示loading框
	 * @param {Object} config：其它配置信息
	 * 	可以包含：
	 * 		isReturn: 是否返回修改。默认不返回
	 */
	alert(table, where, data, loading, config = {}) {
		return handleWithTable({type: 'alert', table, where, data, loading, config})
	}, 
	/**
	 * 查询
	 * 一般要先通过缓存函数
	 * 	table：字符串表示当前云服务空间的数据库名称。对象的话是space和name
	 * 	where：查询参数。。如果是jql语法，需前缀’jql:‘标识
	 * 	pagination：分页参数
	 * 	loading：是否显示加载框
	 * 	config：其它配置信息
			type = 1, // 数值。1：本地请求；2：云端请求。一般get默认是本地请求；其它默认为云端请求
	  		cache = 100, // 该接口会多少秒内的请求直接读取缓存。防止同一个接口相同的参数下短时间内多次请求
			refresh, // 是否刷新操作，刷新操作将不读取缓存
			cacheFix, // 是否缓存固定，固定的将在启动时不会被清空
	 */
	get(table, where = {}, pagination, loading, config = {}) {
		// 查询参数中有remove字段，并且remove大于0，则默认要有remove字段
		if (where.remove > 0) {
			if (!config.defuseKeys) {
				config.defuseKeys = ['HISTORY']
			}
		}
		var type = 'get'
		if (where) {
			if (where.getTreePath) {
				type = 'getTreeParent'
			} else if (where.getTree) {
				type = 'getTreeChild'
			}
		}
		return handleWithTable({type, table, where, pagination, loading, config})
	},
	
	/**
	 * 新增修改 - 查询到就修改，没有则新增
	 * 先走查询，
	 * 有数据则修改
	 * 没有数据则新增
	 * 
	 * @param {Object || String} table：数据库信息。对象表示写成了space和name形式，字符串表示name
	 * @param {Object || Array || String} where：需修改的信息的条件，同查询。同时默认不返回新增或修改后的数据。如果是jql语法，需前缀’jql:‘标识
	 * @param {Object} data：需新增或修改的信息。修改的时候有_id的话，会被自动去除
	 * @param {Boolean} loading：是否显示loading框
	 * @param {Object} config：其它配置信息
	 * 		可以包含：
	 * 			isReturn: 是否返回新增修改
	 */
	addAlert(table, where, data, loading, config) {
		return handleWithTable({type: 'addAlert', table, where, data, loading, config})
	},
	/**
	 * 删除新增 - 查询到满足条件的数据后完全删除，再新增。
	 * 新增时不走校验过程，直接新增，适用于上传数据的覆盖性操作
	 * 
	 * @param {Object || String} table：数据库信息。对象表示写成了space和name形式，字符串表示name
	 * @param {Object || Array || String} where：删除前需要修改的信息的条件，同查询。同时默认不返回新增后的数据。如果是jql语法，需前缀’jql:‘标识
	 * @param {Object} data：需新增的信息。内部有判断添加where中的非对象数据，若要保证准确性，请在调用前处理
	 * @param {Boolean} loading：是否显示loading框
	 * @param {Object} config：其它配置信息
	 * 		可以包含：
	 * 		isDelete: 是否真的删除；
	 * 		isReturn: 是否返回新增；
	 * 		MainKey: 非真的删除时，根据该字段名决定是新增还是修改。该字段的存在，可以保证假的删除不会无限扩增数量
	 */
	removeAdd(table, where, data, loading, config) {
		return handleWithTable({type: 'removeAdd', table, where, data, loading, config})
	},
	
	/**
	 * 生成个一个编码。type=1和2默认注册；type=034默认不注册，确认时需传入code重新请求即是确认
	 * @param {Object} params
	 * @param {Object} loading
	 * @param {Object} config
	 * 
	 * await：默认不注册的情况下是否延迟确认，延迟确认的话，需要手动执行params.callback，并且可传入callback，以接收最后准确的值
	 */
	getCode(params, loading, config) {
		return new Promise((resolve, reject) => {
			fetchData('fetch', 'getCode', params, loading, config).then(res => {
				const code = res.data
				if (params.type === 0 || params.type === 3 || params.type === 4) {
					// 这三种情况下，并不直接备案编码，增加确认的事件
					params.callback = (code, callback) => {
						uni.showModal({
							title: code,
							content: '是否确认使用该编码',
							confirmText: '使用',
							success: res => {
								params.code = code
								fetchData('fetch', 'getCode', params, loading, config).then(res => {
									if (params.await) { // 延迟确认
										if (res.data !== code) {
											callback && callback(res.data)
										}
									} else {
										resolve(res.data)
									}
								}).catch(reject)
							}
						})
					}
					if (params.await) { // 是否延迟确认，延迟确认的话，需要手动执行params.callback，并且可传入callback
						resolve(code)
					} else { // 即时确认的，不需要传入callback，而是执行resolve
						params.callback && params.callback(code)
					}
				} else {
					resolve(code)
				}
			}).catch(reject)
		})
	}
}